<template>
    <!-- <div class="shape" @click="selectCurComponent" @mousedown="handleMouseDownOnShape"> -->
    <div class="shape" @click.stop="selectCurComponent" @mousedown.stop="handleMoveMouseDownOnShape">
        <span class="shape-roate_bar el-icon-refresh" v-show="isActive()" @mousedown.stop="handleRotate"></span>
        <!-- <span class="shape-lock_bar" v-show="element.isLock"></span> -->
        <!-- <div class="shape-options">
            <span class="shape-move_bar el-icon-menu" v-show="isActive()" @mousedown="handleMoveMouseDownOnShape"></span>
        </div> -->
        <div
            class="shape-point"
            v-for="item in (isActive()? pointList : [])"
            @mousedown.stop="handleMouseDownOnPoint(item, $event)"
            :key="item"
            :style="getPointStyle(item)">
        </div>
        <slot>
            <Shape v-for="(sub, index) in (element.children || [])" 
                :key="index" 
                :index="index" 
                :defaultStyle="sub.style" 
                :element="sub" 
                :active="sub === curComponent"
                :style="getShapeStyle(sub.style)">
            </Shape>
        </slot>
    </div>
</template>

<script>
import { mapState } from 'vuex';
import { mod360 } from '@/utils/grids/translate';
import calculateComponentPositonAndSize from '@/utils/grids/calculateComponentPositonAndSize';

export default {
    name: 'Shape',
    props: {
        element: {
            require: true,
            type: Object
        },
        defaultStyle: {
            require: true,
            type: Object,
        },
        index: {
            require: true,
            type: [Number, String],
        },
        active: {
            type: Boolean,
            default: false,
        }
    },
    data() {
        return {
            pointList: ['lt', 't', 'rt', 'r', 'rb', 'b', 'lb', 'l'], // 八个方向
            initialAngle: { // 每个点对应的初始角度
                lt: 0,
                t: 45,
                rt: 90,
                r: 135,
                rb: 180,
                b: 225,
                lb: 270,
                l: 315,
            },
            angleToCursor: [ // 每个范围的角度对应的光标
                { start: 338, end: 23, cursor: 'nw' },
                { start: 23, end: 68, cursor: 'n' },
                { start: 68, end: 113, cursor: 'ne' },
                { start: 113, end: 158, cursor: 'e' },
                { start: 158, end: 203, cursor: 'se' },
                { start: 203, end: 248, cursor: 's' },
                { start: 248, end: 293, cursor: 'sw' },
                { start: 293, end: 338, cursor: 'w' },
            ],
            cursors: {},
            editor: null
        }
    },
    computed: {
        ...mapState({
            curComponent: state => state.grids.curComponent
        })
    },
    mounted() {
        // this.editor = document.querySelector('#editor')
        this.editor = this.$el.parentNode
    },
    methods: {
        getShapeStyle(style) {
            const result = {};
            ['width', 'height', 'top', 'left', 'rotate', 'backgroundColor', 
                'borderTop', 'borderTopColor', 'borderRight', 'borderRightColor', 'borderBottom', 'borderBottomColor', 'borderLeft', 'borderLeftColor',
            ].forEach(attr => {
                if (['width', 'height', 'top', 'left'].indexOf(attr) != -1) {
                    result[attr] = style[attr] + 'px'
                } else if(attr === 'rotate') {
                    result.transform = 'rotate(' + style[attr] + 'deg)'
                } else if(['borderTop', 'borderRight', 'borderBottom', 'borderLeft'].indexOf(attr) != -1) {
                    result[attr] = `${style[attr]}px solid`
                } else {
                    result[attr] = style[attr]
                }
            })
            return result
        },
        isActive() {
            return this.active && !this.element.isLock
        },
        selectCurComponent(e) {
            // 阻止向父组件冒泡
            e.stopPropagation()
            e.preventDefault()
            // this.$store.commit('hideContextMenu')
        },
        // 处理旋转
        handleRotate(e) {
            // this.$store.commit('setClickComponentStatus', true)
            e.preventDefault()
            e.stopPropagation()
            // 初始坐标和初始角度
            const pos = { ...this.defaultStyle }
            const startY = e.clientY
            const startX = e.clientX
            const startRotate = pos.rotate

            // 获取元素中心点位置
            const rect = this.$el.getBoundingClientRect()
            const centerX = rect.left + rect.width / 2
            const centerY = rect.top + rect.height / 2

            // 旋转前的角度
            const rotateDegreeBefore = Math.atan2(startY - centerY, startX - centerX) / (Math.PI / 180)

            // 如果元素没有移动，则不保存快照
            let hasMove = false
            const move = (moveEvent) => {
                hasMove = true
                const curX = moveEvent.clientX
                const curY = moveEvent.clientY
                // 旋转后的角度
                const rotateDegreeAfter = Math.atan2(curY - centerY, curX - centerX) / (Math.PI / 180)
                // 获取旋转的角度值
                pos.rotate = startRotate + rotateDegreeAfter - rotateDegreeBefore
                // 修改当前组件样式
                this.$store.dispatch('setShapeStyle', pos)
            }

            const up = () => {
                // hasMove && this.$store.commit('recordSnapshot')
                document.removeEventListener('mousemove', move)
                document.removeEventListener('mouseup', up)
                this.cursors = this.getCursor() // 根据旋转角度获取光标位置
            }

            document.addEventListener('mousemove', move)
            document.addEventListener('mouseup', up)
        },
        getPointStyle(point) {
            const { width, height } = this.defaultStyle
            const hasT = /t/.test(point)
            const hasB = /b/.test(point)
            const hasL = /l/.test(point)
            const hasR = /r/.test(point)
            let newLeft = 0
            let newTop = 0
            
            // 四个角的点
            if (point.length === 2) {
                newLeft = hasL? 0 : width
                newTop = hasT? 0 : height
            } else {
                // 上下两点的点，宽度居中
                if (hasT || hasB) {
                    newLeft = width / 2
                    newTop = hasT? 0 : height
                }

                // 左右两边的点，高度居中
                if (hasL || hasR) {
                    newLeft = hasL? 0 : width
                    newTop = Math.floor(height / 2)
                }
            }
            
            const style = {
                marginLeft: hasR? '-4px' : '-4px',
                marginTop: '-4px',
                left: `${newLeft}px`,
                top: `${newTop}px`,
                cursor: this.cursors[point],
            }
            
            return style
        },
        isNeedLockProportion() {
            if (this.element.component != 'Group') return false
            const ratates = [0, 90, 180, 360]
            for (const component of this.element.propValue) {
                if (!ratates.includes(mod360(parseInt(component.style.rotate)))) {
                    return true
                }
            }

            return false
        },
        handleMouseDownOnPoint(point, e) {
            console.log('sizeing');
            // this.$store.commit('setClickComponentStatus', true)
            e.stopPropagation()
            e.preventDefault()
 
            const style = { ...this.defaultStyle }

            // 组件宽高比
            const proportion = style.width / style.height

            // 组件中心点
            const center = {
                x: style.left + style.width / 2,
                y: style.top + style.height / 2,
            }

            // 获取画布位移信息
            const editorRectInfo = this.editor.getBoundingClientRect()

            // 当前点击坐标
            const curPoint = {
                x: e.clientX - editorRectInfo.left,
                y: e.clientY - editorRectInfo.top,
            }

            // 获取对称点的坐标
            const symmetricPoint = {
                x: center.x - (curPoint.x - center.x),
                y: center.y - (curPoint.y - center.y),
            }

            // 是否需要保存快照
            let needSave = false
            let isFirst = true

            const needLockProportion = this.isNeedLockProportion()
            const move = (moveEvent) => {
                // 第一次点击时也会触发 move，所以会有“刚点击组件但未移动，组件的大小却改变了”的情况发生
                // 因此第一次点击时不触发 move 事件
                if (isFirst) {
                    isFirst = false
                    return
                }

                needSave = true
                const curPositon = {
                    x: moveEvent.clientX - editorRectInfo.left,
                    y: moveEvent.clientY - editorRectInfo.top,
                }
                
                calculateComponentPositonAndSize(point, style, curPositon, proportion, needLockProportion, {
                    center,
                    curPoint,
                    symmetricPoint,
                })

                this.$store.dispatch('setShapeStyle', style)
            }

            const up = () => {
                document.removeEventListener('mousemove', move)
                document.removeEventListener('mouseup', up)
                // needSave && this.$store.commit('recordSnapshot')
            }

            document.addEventListener('mousemove', move)
            document.addEventListener('mouseup', up)
        },
        getCursor() {
            const { angleToCursor, initialAngle, pointList, curComponent } = this
            const rotate = mod360(curComponent.style.rotate) // 取余 360
            const result = {}
            let lastMatchIndex = -1 // 从上一个命中的角度的索引开始匹配下一个，降低时间复杂度
            pointList.forEach(point => {
                const angle = mod360(initialAngle[point] + rotate)
                const len = angleToCursor.length
                
                while (lastMatchIndex <= 6) {
                    lastMatchIndex = (lastMatchIndex + 1) % len
                    const angleLimit = angleToCursor[lastMatchIndex]
                    if (angle < 23 || angle >= 338) {
                        result[point] = 'nw-resize'
                        
                        return
                    }

                    if (angleLimit.start <= angle && angle < angleLimit.end) {
                        result[point] = angleLimit.cursor + '-resize'
                        
                        return
                    }
                }
            })
            return result
        },
        handleMouseDownOnShape(e) {
            e.stopPropagation()
            this.$store.dispatch('setCurComponent', { component: this.element, index: this.index })
                .then(() => {
                    this.cursors = this.getCursor() // 根据旋转角度获取光标位置
                })
        },
        handleMoveMouseDownOnShape(e) {
            console.log('moveing');
            e.stopPropagation()
            this.$store.dispatch('setCurComponent', { component: this.element, index: this.index })
                .then(() => {
                    this.cursors = this.getCursor() // 根据旋转角度获取光标位置
                })


            const pos = { ...this.defaultStyle }
            const startY = e.clientY
            const startX = e.clientX
            // 如果直接修改属性，值的类型会变为字符串，所以要转为数值型
            const startTop = Number(pos.top)
            const startLeft = Number(pos.left)
            
            // 如果元素没有移动，则不保存快照
            let hasMove = false
            const move = (moveEvent) => {
                hasMove = true
                const curX = moveEvent.clientX
                const curY = moveEvent.clientY
                pos.top = curY - startY + startTop
                pos.left = curX - startX + startLeft

                // 修改当前组件样式
                this.$store.dispatch('setShapeStyle', pos)
                
                // 等更新完当前组件的样式并绘制到屏幕后再判断是否需要吸附
                // 如果不使用 $nextTick，吸附后将无法移动
                this.$nextTick(() => {
                    // 触发元素移动事件，用于显示标线、吸附功能
                    // 后面两个参数代表鼠标移动方向
                    // curY - startY > 0 true 表示向下移动 false 表示向上移动
                    // curX - startX > 0 true 表示向右移动 false 表示向左移动
                    this.$bus.$emit('move', curY - startY > 0, curX - startX > 0)
                })
            }

            const up = () => {
                document.removeEventListener('mousemove', move)
                document.removeEventListener('mouseup', up)
            }

            document.addEventListener('mousemove', move)
            document.addEventListener('mouseup', up)
        }
    }
}
</script>

<style lang="scss" scoped>
.shape {
    position: absolute;
    border: 1px solid #B2B4B6;

    &:hover {
        cursor: move;
    }
}
.active {
    outline: 1px solid #70c0ff;
    user-select: none;
}
.shape-point {
    position: absolute;
    background: #fff;
    border: 1px solid #59c7f9;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    z-index: 1;
}
.shape-roate_bar {
    position: absolute;
    top: -34px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 16px;
    font-weight: 600;
    cursor: grab;
    color: #59c7f9;
    font-size: 20px;
    font-weight: 600;

    &:active {
        cursor: grabbing;
    }
}
.shape-options{
    position: absolute;
    top: 4px;
    right: 4px;
    background-color: #fff;
    z-index: 100;
}
.shape-move_bar{
    color: #59c7f9;
    font-size: 16px;
    font-weight: 600;
    &:hover {
        cursor: move;
    }
}
.shape-lock_bar {
    position: absolute;
    top: 0;
    right: 0;
}
</style>